use crate::{
    api::contracts::ContractVo,
    db::models::{Invoice, Material, Payment, Provider},
    schema::{contract, invoice, material, payment, project},
};
use actix_web::{
    post,
    web::{self, Data, Json},
    HttpResponse,
};
use diesel::{dsl::exists, insert_into, select, sql_types::Bool, update};
use diesel::{dsl::sql, prelude::*, sql_types::Integer};

use crate::schema::provider::dsl::*;
use crate::{api::Page, schema::provider, DbPool};

#[derive(Debug, serde::Deserialize)]
pub struct PageProviderReq {
    page: i64,
    size: i64,
    keyword: String,
}

const SIZE_MAX: i64 = 50000000000;
const SIZE_INVERSE: i64 = -1;

#[post("/page")]
async fn page_provider(
    db: Data<DbPool>,
    request: web::Json<PageProviderReq>,
) -> Json<Page<Provider>> {
    let req = request.0;
    let data = web::block(move || {
        let mut conn = db.get().unwrap();
        let mut size = req.size;
        if req.size.eq(&SIZE_INVERSE) {
            size = SIZE_MAX;
        }
        let data: Result<Vec<Provider>, _> = provider
            .filter(provider::name.like(format!("%{}%", req.keyword)))
            .limit(size)
            .offset((req.page - 1) * size)
            .load::<Provider>(&mut conn);
        let c: Result<i64, _> = provider
            .select(diesel::dsl::count(provider::id))
            .filter(provider::name.like(format!("%{}%", req.keyword)))
            .first(&mut conn);

        match data {
            Ok(d) => Page {
                total: c.unwrap(),
                content: d,
            },
            _ => Page {
                total: 0,
                content: vec![],
            },
        }
    })
    .await;
    Json(data.unwrap())
}

#[post("/create")]
async fn create_provider(db: Data<DbPool>, request: web::Json<Provider>) -> HttpResponse {
    let req = request.0;
    let data = web::block(move || {
        let mut conn = db.get().unwrap();
        let exists: Result<bool, diesel::result::Error> =
            select(exists(provider.filter(provider::name.eq(&req.name)))).get_result(&mut conn);
        if let Ok(s) = exists {
            if s {
                return 0;
            }
        }
        let result = insert_into(provider).values(&req).execute(&mut conn);
        match result {
            Ok(d) => d,
            _ => 0,
        }
    })
    .await;
    match data.unwrap() {
        0 => HttpResponse::BadRequest().body("名称已存在"),
        _ => HttpResponse::Ok().json(true),
    }
}

#[post("/update")]
async fn update_provider(db: Data<DbPool>, request: web::Json<Provider>) -> HttpResponse {
    let req = request.0;
    let data = web::block(move || {
        let mut conn = db.get().unwrap();
        let exists: Result<bool, diesel::result::Error> = select(exists(
            provider.filter(provider::name.eq(&req.name).and(provider::id.ne(&req.id))),
        ))
        .get_result(&mut conn);
        if let Ok(s) = exists {
            if s {
                return 0;
            }
        }
        let result: Result<usize, _> = update(provider.filter(provider::id.eq(req.id)))
            .set((
                provider::name.eq(req.name),
                provider::contact_name.eq(req.contact_name),
                provider::contact_phone.eq(req.contact_phone),
                provider::remarks.eq(req.remarks),
            ))
            .execute(&mut conn);
        match result {
            Ok(d) => d,
            _ => 0,
        }
    })
    .await;
    match data.unwrap() {
        0 => HttpResponse::BadRequest().body("名称已存在"),
        _ => HttpResponse::Ok().json(true),
    }
}

#[derive(Debug, serde::Deserialize)]
pub struct DeleteProviderReq {
    id: i32,
}

#[post("/delete")]
async fn delete_provider(db: Data<DbPool>, request: web::Json<DeleteProviderReq>) -> HttpResponse {
    let req = request.0;
    let data = web::block(move || {
        let mut conn = db.get().unwrap();
        let result = diesel::delete(provider.filter(provider::id.eq(req.id))).execute(&mut conn);
        match result {
            Ok(d) => d,
            _ => 0,
        }
    })
    .await;
    match data.unwrap() {
        0 => HttpResponse::BadRequest().body("未找到记录"),
        _ => HttpResponse::Ok().json(true),
    }
}

#[derive(Debug, serde::Deserialize)]
pub struct OverviewProviderReq {
    id: i32,
}

#[derive(Debug, serde::Serialize)]
pub struct OverviewProviderVo {
    id: i32,
    key: i32,
    project_id: i32,
    project_name: String,
    contract_no: String,
    category_list: String,
    contract_amount_total: f64,
    provider_name: String,
    material_amount_total: f64,
    verify_amount_total: f64,
    defaulters_amount_total: f64,
    invoice_amount_total: f64,
    invoice_hang_amount_total: f64,
}

#[derive(Debug, serde::Serialize)]
pub struct OverviewProviderResult {
    all_contract_amount_total: f64,
    all_verify_amount_total: f64,
    all_invoice_amount_total: f64,
    all_defaulters_amount_total: f64,
    all_invoice_hang_amount_total: f64,
    content: Vec<OverviewProviderVo>,
}

#[post("/provider")]
async fn overview_provider(
    db: Data<DbPool>,
    request: web::Json<OverviewProviderReq>,
) -> HttpResponse {
    let req = request.0;
    let data = web::block(move || {
        let mut conn = db.get().unwrap();
        let all_contracts: Result<Vec<ContractVo>, _> = contract::table
            .inner_join(provider::table)
            .inner_join(project::table)
            .filter(
                sql::<Bool>("contract.provider_id = ")
                    .bind::<Integer, _>(req.id)
                    .sql(" or -1 = ")
                    .bind::<Integer, _>(req.id)
                    .sql(" order by provider.name asc"),
            )
            .select((
                contract::id,
                contract::project_id,
                contract::provider_id,
                contract::no,
                contract::sub_contract,
                contract::parent_no,
                contract::contract_date,
                project::name,
                provider::name,
                contract::contract_amount,
                contract::remarks,
            ))
            .load::<ContractVo>(&mut conn);
        let mut page_content = vec![];
        if let Ok(all_contracts) = all_contracts {
            for c in &all_contracts {
                if let Some(_) = c.parent_no {
                    continue;
                }
                let c_materials: Result<Vec<Material>, _> = material::table
                    .filter(material::contract_id.eq(c.id))
                    .load::<Material>(&mut conn);
                let c_payments: Result<Vec<Payment>, _> = payment::table
                    .filter(payment::contract_id.eq(c.id))
                    .load::<Payment>(&mut conn);
                let c_invoices: Result<Vec<Invoice>, _> = invoice::table
                    .filter(invoice::contract_id.eq(c.id))
                    .load::<Invoice>(&mut conn);
                let mut the_category_list = vec![];
                let mut contract_amount_total = c.contract_amount;
                let mut material_amount_total = 0.0;
                let mut verify_amount_total = 0.0;
                let mut his_defaulters_amount_total = 0.0;
                let mut his_hang_amount_total = 0.0;
                let mut invoice_amount_total = 0.0;
                let mut payment_amount_total = 0.0;
                if let Ok(c_materials) = c_materials {
                    for cm in &c_materials {
                        material_amount_total += cm.verify_number * cm.price;
                        if let Some(1) = cm.his_flag {
                            if let Some(his_defaulter_amount) = cm.his_defaulters_amount {
                                his_defaulters_amount_total += his_defaulter_amount;
                            }
                            if let Some(his_hang_amount) = cm.his_hang_invoice_amount {
                                his_hang_amount_total += his_hang_amount;
                            }
                        } else {
                            if let Some(vm) = cm.verify_amount {
                                verify_amount_total += vm;
                            }
                        }
                        if let Some(cmc) = &cm.category {
                            if !the_category_list.contains(cmc) {
                                the_category_list.insert(the_category_list.len(), cmc.clone());
                            }
                        }
                    }
                }
                if let Ok(c_invoices) = c_invoices {
                    for ci in &c_invoices {
                        invoice_amount_total += ci.amount;
                    }
                }
                if let Ok(c_payments) = c_payments {
                    for cp in &c_payments {
                        payment_amount_total += cp.pay_amount;
                    }
                }
                // 处理附加合同
                for ele in &all_contracts {
                    if let Some(p_no) = &ele.parent_no {
                        if p_no.eq(&c.no) {
                            contract_amount_total += ele.contract_amount;
                            let sub_c_materials: Result<Vec<Material>, _> = material::table
                                .filter(material::contract_id.eq(ele.id))
                                .load::<Material>(&mut conn);
                            let sub_c_payments: Result<Vec<Payment>, _> = payment::table
                                .filter(payment::contract_id.eq(ele.id))
                                .load::<Payment>(&mut conn);
                            let sub_c_invoices: Result<Vec<Invoice>, _> = invoice::table
                                .filter(invoice::contract_id.eq(ele.id))
                                .load::<Invoice>(&mut conn);
                            if let Ok(sub_c_materials) = sub_c_materials {
                                for cm in &sub_c_materials {
                                    material_amount_total += cm.verify_number * cm.price;
                                    if let Some(1) = cm.his_flag {
                                        if let Some(his_defaulter_amount) = cm.his_defaulters_amount
                                        {
                                            his_defaulters_amount_total += his_defaulter_amount;
                                        }
                                        if let Some(his_hang_amount) = cm.his_hang_invoice_amount {
                                            his_hang_amount_total += his_hang_amount;
                                        }
                                    } else {
                                        if let Some(vm) = cm.verify_amount {
                                            verify_amount_total += vm;
                                        }
                                    }
                                    if let Some(cmc) = &cm.category {
                                        if !the_category_list.contains(cmc) {
                                            the_category_list
                                                .insert(the_category_list.len(), cmc.clone());
                                        }
                                    }
                                }
                            }
                            if let Ok(sub_c_invoices) = sub_c_invoices {
                                for ci in &sub_c_invoices {
                                    invoice_amount_total += ci.amount;
                                }
                            }
                            if let Ok(sub_c_payments) = sub_c_payments {
                                for cp in &sub_c_payments {
                                    payment_amount_total += cp.pay_amount;
                                }
                            }
                        }
                    }
                }
                let defaulters_amount_total =
                    verify_amount_total - payment_amount_total + his_defaulters_amount_total;
                let invoice_hang_amount_total =
                    invoice_amount_total - payment_amount_total + his_hang_amount_total;
                let opv = OverviewProviderVo {
                    id: c.id,
                    key: c.id,
                    contract_no: c.no.clone(),
                    project_id: c.project_id,
                    project_name: c.project_name.clone(),
                    category_list: the_category_list.join(","),
                    contract_amount_total,
                    provider_name: c.provider_name.clone(),
                    material_amount_total,
                    verify_amount_total,
                    defaulters_amount_total,
                    invoice_amount_total,
                    invoice_hang_amount_total,
                };
                page_content.insert(page_content.len(), opv);
            }
        }

        let mut all_contract_amount_total = 0.0;
        let mut all_verify_amount_total = 0.0;
        let mut all_invoice_amount_total = 0.0;
        let mut all_defaulters_amount_total = 0.0;
        let mut all_invoice_hang_amount_total = 0.0;
        for pc in &page_content {
            all_contract_amount_total += pc.contract_amount_total;
            all_verify_amount_total += pc.verify_amount_total;
            all_invoice_amount_total += pc.invoice_amount_total;
            all_defaulters_amount_total += pc.defaulters_amount_total;
            all_invoice_hang_amount_total += pc.invoice_hang_amount_total;
        }

        return OverviewProviderResult {
            content: page_content,
            all_contract_amount_total,
            all_verify_amount_total,
            all_invoice_amount_total,
            all_defaulters_amount_total,
            all_invoice_hang_amount_total,
        };
    })
    .await;

    match data {
        Err(_) => HttpResponse::BadRequest().body("未找到记录"),
        Ok(d) => HttpResponse::Ok().json(d),
    }
}
